'use strick';

//获取html元素的transforms属性的正则表达式
const TRANSLATEX_REGEXP = /translateX\(\-?\d+px\)/i;
const TRANSLATEY_REGEXP = /translateY\(\-?\d+px\)/i;
const ROTATEX_REGEXP = /rotateX\(\-?\d+deg\)/i;
const ROTATEY_REGEXP = /rotateY\(\-?\d+deg\)/i;
const ROTATEZ_REGEXP = /rotateZ\(\-?\d+deg\)/i;
const SCALE_REGEXP = /scale\(\d+\,\d+\)/i;

/*  eleMov(): 移动动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: start: 初始位置，一般值为0，类型number
    参数三: end: 需要移动的距离，类型number
    参数四: duration: 完成时间（ms），一般大于等于1000ms，类型number
    参数五: direction: 移动方向，值为'h'水平方向 或者 'v'垂直方向，类型string
    参数六: timing: 运动曲线，值为'linear'线性，或者 'ease'渐慢，类型string
    参数七: vhRatio: v垂直移动距离 与 h水平移动距离的比值，类型number
    参数八: step: 步数间隔，最小值为1，类型number，此为1时，运动曲线为linear
    参数九: callback: 可选，动画完成后的回调函数
*/
function eleMov(htmlElement, start, end, duration, direction, timing, vhRatio, step, callback) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    };
    if ((typeof start) !== 'number' || (typeof end) !== 'number') {
        console.error('startANDendError: 缺少/类型错误（必须为number类型） start参数 或 end参数');
        return;
    };
    if ((typeof duration) !== 'number') {
        console.error('durationError: 缺少/类型错误（必须为number类型） duration参数');
        return;
    };
    if ((typeof vhRatio) !== 'number') {
        console.error('vhRatioError: 缺少/类型错误（必须为number类型） vhRatio参数');
        return;
    };
    if ((typeof step) !== 'number') {
        console.error('stepError: 缺少/类型错误（必须为number类型） step参数');
        return;
    };
    if (timing !== 'linear' && timing !== 'ease') {
        console.error('timingError: timing参数的值为"linear" / "ease"');
        return;
    };
    if (direction !== 'h' && direction !== 'v' && direction !== 'vh') {
        console.error('directionError: 输入合法方向值"h" / "v" / "vh"');
        return;
    };
    if (direction === 'vh' && vhRatio === 0) {
        console.error('vhRatioError: direction参数为"vh"时，vhRatio参数的值不能为 0');
        return;
    };

    //初始化数据
    let n = 1;
    let finish = false;
    let t = end - start;
    let temp = step;
    let addTranslateX = '';
    let addTranslateY = '';
    let addRotateX = '';
    let addRotateY = '';
    let addRotateZ = '';
    let addScale = '';
    let htmlTranslateX = TRANSLATEX_REGEXP.exec(htmlElement.style.transform);
    let htmlTranslateY = TRANSLATEY_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateX = ROTATEX_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateY = ROTATEY_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateZ = ROTATEZ_REGEXP.exec(htmlElement.style.transform);
    let htmlScale = SCALE_REGEXP.exec(htmlElement.style.transform);
    let intervale = duration / t / step;
    if (direction !== 'vh') vhRatio = null;
    if (step < 1) step = 1;
    if (intervale < 0) {
        intervale *= -1;
        n = -1;
    };
    if (htmlTranslateX !== null) addTranslateX = htmlTranslateX[0];
    if (htmlTranslateY !== null) addTranslateY = htmlTranslateY[0];
    if (htmlRotateX !== null) addRotateX = htmlRotateX[0];
    if (htmlRotateY !== null) addRotateY = htmlRotateY[0];
    if (htmlRotateZ !== null) addRotateZ = htmlRotateZ[0];
    if (htmlScale !== null) addScale = htmlScale[0];

    //移动动画主要代码
    let mov = (start, end, setIntervalName, XorY, addTranslate) => {
        if (XorY !== 'XY') {
            htmlElement.style.transform = `translate${XorY}(${start}px)` + addTranslate + addRotateX + addRotateY + addRotateZ + addScale;
            if (start * n >= end * n) {
                htmlElement.style.transform = `translate${XorY}(${end}px)` + addTranslate + addRotateX + addRotateY + addRotateZ + addScale;
                clearInterval(setIntervalName);
                finish = true;
                if (typeof callback === 'function') callback(finish);
                return;
            };
        } else {
            htmlElement.style.transform = `translateX(${start}px) translateY(${start * vhRatio}px)` + addRotateX + addRotateY + addRotateZ + addScale;
            if (start * n >= end * n) {
                htmlElement.style.transform = `translateX(${end}px) translateY(${end * vhRatio}px)` + addRotateX + addRotateY + addRotateZ + addScale;
                clearInterval(setIntervalName);
                finish = true;
                if (typeof callback === 'function') callback(finish);
                return;
            };
        };
    };

    //动画移动开始
    if (direction === 'h') {
        if (timing === 'linear' || step === 1) {
            let movHorizontal = setInterval(() => {
                start = start + step * n;
                mov(start, end, movHorizontal, 'X', addTranslateY);
            }, intervale);
        } else {
            let movHorizontal = setInterval(() => {
                if (step > 0) {
                    step -= temp / t * n;
                } else {
                    step = 0.1;
                };
                start = start + step * n;
                mov(start, end, movHorizontal, 'X', addTranslateY);
            }, intervale);
        };
    } else if (direction === 'v') {
        if (timing === 'linear') {
            let movVertical = setInterval(() => {
                start = start + step * n;
                mov(start, end, movVertical, 'Y', addTranslateX);
            }, intervale);
        } else {
            let movVertical = setInterval(() => {
                if (step > 0) {
                    step -= temp / t * n;
                } else {
                    step = 0.1;
                }
                start = start + step * n;
                mov(start, end, movVertical, 'Y', addTranslateX);
            }, intervale);
        };
    } else {
        if (timing === 'linear') {
            let movVH = setInterval(() => {
                start = start + step * n;
                mov(start, end, movVH, 'XY');
            }, intervale);
        } else {
            let movVH = setInterval(() => {
                if (step > 0) {
                    step -= temp / t * n;
                } else {
                    step = 0.1;
                }
                start = start + step * n;
                mov(start, end, movVH, 'XY');
            }, intervale);
        };
    }
};

/*  eleRotate(): 旋转动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: start: 初始位置，一般值为0，类型number
    参数三: end: 需要旋转的角度，类型number
    参数四: duration: 完成时间（ms），一般大于等于1000ms，类型number
    参数五: direction: 旋转轴方向，值为'x' 或者 'y' 或者 'z'，类型string
    参数六: timing: 运动曲线，值为'linear'线性，或者 'ease'渐慢，类型string
    参数七: step: 步数间隔，最小值为1，类型number，此为1时，运动曲线为linear
    参数八: callback: 可选，动画完成后的回调函数
*/
function eleRotate(htmlElement, start, end, duration, direction, timing, step, callback) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    };
    if ((typeof start) !== 'number' || (typeof end) !== 'number') {
        console.error('startANDendError: 缺少/类型错误（必须为number类型） start参数 或 end参数');
        return;
    };
    if ((typeof duration) !== 'number') {
        console.error('durationError: 缺少/类型错误（必须为number类型） duration参数');
        return;
    };
    if ((typeof step) !== 'number') {
        console.error('stepError: 缺少/类型错误（必须为number类型） step参数');
        return;
    };
    if (timing !== 'linear' && timing !== 'ease') {
        console.error('timingError: timing参数的值为"linear" / "ease"');
        return;
    };
    if (direction !== 'z' && direction !== 'x' && direction !== 'y') {
        console.error('directionError: 输入合法旋转轴值"x" / "y" / "z"');
        return;
    };

    //初始化数据
    let n = 1;
    let finish = false;
    let t = end - start;
    let temp = step;
    let addTranslateX = '';
    let addTranslateY = '';
    let addRotateX = '';
    let addRotateY = '';
    let addRotateZ = '';
    let addScale = '';
    let htmlTranslateX = TRANSLATEX_REGEXP.exec(htmlElement.style.transform);
    let htmlTranslateY = TRANSLATEY_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateX = ROTATEX_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateY = ROTATEY_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateZ = ROTATEZ_REGEXP.exec(htmlElement.style.transform);
    let htmlScale = SCALE_REGEXP.exec(htmlElement.style.transform);
    let intervale = duration / t / step;
    if (step < 1) step = 1;
    if (intervale < 0) {
        intervale *= -1;
        n = -1;
    };
    if (htmlTranslateX !== null) addTranslateX = htmlTranslateX[0];
    if (htmlTranslateY !== null) addTranslateY = htmlTranslateY[0];
    if (htmlRotateX !== null) addRotateX = htmlRotateX[0];
    if (htmlRotateY !== null) addRotateY = htmlRotateY[0];
    if (htmlRotateZ !== null) addRotateZ = htmlRotateZ[0];
    if (htmlScale !== null) addScale = htmlScale[0];

    //旋转动画主要代码
    let rotate = (start, end, setIntervalName, XoYoZ, addRotateArr) => {
        htmlElement.style.transform = addTranslateX + addTranslateY + `rotate${XoYoZ}(${start}deg)` + addRotateArr[0] + addRotateArr[1] + addScale;
        if (start * n >= end * n) {
            htmlElement.style.transform = addTranslateX + addTranslateY + `rotate${XoYoZ}(${end}deg)` + addRotateArr[0] + addRotateArr[1] + addScale;
            clearInterval(setIntervalName);
            finish = true;
            if (typeof callback === 'function') callback(finish);
            return;
        };
    };

    //动画旋转开始
    if (direction === 'z') {
        if (timing === 'linear' || step === 1) {
            let rotateAni = setInterval(() => {
                start = start + step * n;
                rotate(start, end, rotateAni, 'Z', [addRotateX, addRotateY]);
            }, intervale);
        } else {
            let rotateAni = setInterval(() => {
                if (step > 0) {
                    step -= temp / t * n;
                } else {
                    step = 0.1;
                };
                start = start + step * n;
                rotate(start, end, rotateAni, 'Z', [addRotateX, addRotateY]);
            }, intervale);
        };
    } else if (direction === 'x') {
        if (timing === 'linear' || step === 1) {
            let rotateAni = setInterval(() => {
                start = start + step * n;
                rotate(start, end, rotateAni, 'X', [addRotateY, addRotateZ]);
            }, intervale);
        } else {
            let rotateAni = setInterval(() => {
                if (step > 0) {
                    step -= temp / t * n;
                } else {
                    step = 0.1;
                };
                start = start + step * n;
                rotate(start, end, rotateAni, 'X', [addRotateY, addRotateZ]);
            }, intervale);
        };
    } else {
        if (timing === 'linear' || step === 1) {
            let rotateAni = setInterval(() => {
                start = start + step * n;
                rotate(start, end, rotateAni, 'Y', [addRotateX, addRotateZ]);
            }, intervale);
        } else {
            let rotateAni = setInterval(() => {
                if (step > 0) {
                    step -= temp / t * n;
                } else {
                    step = 0.1;
                };
                start = start + step * n;
                rotate(start, end, rotateAni, 'Y', [addRotateX, addRotateZ]);
            }, intervale);
        };
    };
};

/*  eleScale(): 缩放动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: scaleXArr: X方向缩放倍数，值如[1,2]，类型array
    参数三: scaleYArr: Y方向缩放倍数，值如[1,2]，类型array
    参数四: duration: 完成时间（ms），一般大于等于1000ms，类型number
    参数五: timing: 运动曲线，值为'linear'线性，或者 'ease'渐慢，类型string
    参数六: step: 步数间隔，最小值为1，类型number，此为1时，运动曲线为linear
    参数七: callback: 可选，动画完成后的回调函数
*/
function eleScale(htmlElement, scaleXArr, scaleYArr, duration, timing, step, callback) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    }
    if (!(scaleXArr instanceof Array) || !(scaleXArr instanceof Array)) {
        console.error('startANDendError: 缺少/类型错误（必须为Array类型） scaleXArr参数 或 scaleYArr参数');
        return;
    }
    if (step < 0.01 || (typeof step) !== 'number') {
        step = 0.01;
        console.warn('stepError: step参数的最小值为 0.05 且类型为number');
    }
    if (timing !== 'linear' && timing !== 'ease') {
        console.error('timingError: timing参数的值为"linear" / "ease"');
        return;
    };

    //初始化数据
    let n = 1;
    let i = 1;
    let finish = false;
    let addTranslateX = '';
    let addTranslateY = '';
    let addRotateX = '';
    let addRotateY = '';
    let addRotateZ = '';
    let tempX = scaleXArr[0];
    let tempY = scaleYArr[0];
    let htmlTranslateX = TRANSLATEX_REGEXP.exec(htmlElement.style.transform);
    let htmlTranslateY = TRANSLATEY_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateX = ROTATEX_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateY = ROTATEY_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateZ = ROTATEZ_REGEXP.exec(htmlElement.style.transform);
    let intervaleX = duration / ((scaleXArr[1] - scaleXArr[0]) / step);
    let intervaleY = scaleYArr[1] - scaleYArr[0];
    if (intervaleX < 0) {
        intervaleX *= -1;
        n = -1;
    };
    if (intervaleY < 0) i = -1;
    if (htmlTranslateX !== null) addTranslateX = htmlTranslateX[0];
    if (htmlTranslateY !== null) addTranslateY = htmlTranslateY[0];
    if (htmlRotateX !== null) addRotateX = htmlRotateX[0];
    if (htmlRotateY !== null) addRotateY = htmlRotateY[0];
    if (htmlRotateZ !== null) addRotateZ = htmlRotateZ[0];

    //缩放动画主要代码
    let scale = (X, Y, setIntervalName) => {
        if (scaleXArr[1] !== tempX && scaleYArr[1] !== tempY) {
            htmlElement.style.transform = addTranslateX + addTranslateY + addRotateX + addRotateY + addRotateZ + `scale(${X},${Y})`;
        } else if (scaleXArr[1] !== tempX && scaleYArr[1] === tempY) {
            htmlElement.style.transform = addTranslateX + addTranslateY + addRotateX + addRotateY + addRotateZ + `scale(${X},${scaleYArr[1]})`;
        } else {
            htmlElement.style.transform = addTranslateX + addTranslateY + addRotateX + addRotateY + addRotateZ + `scale(${scaleXArr[1]},${Y})`;
        };
        if (X * n >= scaleXArr[1] * n) {
            htmlElement.style.transform = addTranslateX + addTranslateY + addRotateX + addRotateY + addRotateZ + `scale(${scaleXArr[1]},${Y})`;
            if (Y * i >= scaleYArr[1] * i) {
                htmlElement.style.transform = addTranslateX + addTranslateY + addRotateX + addRotateY + addRotateZ + `scale(${scaleXArr[1]},${scaleYArr[1]})`;
                clearInterval(setIntervalName);
                finish = true;
                if (typeof callback === 'function') callback(finish);
                return;
            };
        };
        if (Y * i >= scaleYArr[1] * i) {
            htmlElement.style.transform = addTranslateX + addTranslateY + addRotateX + addRotateY + addRotateZ + `scale(${X},${scaleYArr[1]})`;
            if (X * n >= scaleXArr[1] * n) {
                htmlElement.style.transform = addTranslateX + addTranslateY + addRotateX + addRotateY + addRotateZ + `scale(${scaleXArr[1]},${scaleYArr[1]})`;
                clearInterval(setIntervalName);
                finish = true;
                if (typeof callback === 'function') callback(finish);
                return;
            };
        };
    };

    //动画缩放开始
    if (timing === 'linear' || step === 1) {
        let scaleAni = setInterval(() => {
            scaleXArr[0] = scaleXArr[0] + step * n;
            scaleYArr[0] = scaleYArr[0] + step * i;
            scale(scaleXArr[0], scaleYArr[0], scaleAni);
        }, intervaleX);
    } else {
        let scaleAni = setInterval(() => {
            if (step > 0.01) {
                step -= 0.01;
            } else {
                step = 0.01;
            };
            scaleXArr[0] = scaleXArr[0] + step * n;
            scaleYArr[0] = scaleYArr[0] + step * i;
            scale(scaleXArr[0], scaleYArr[0], scaleAni);
        }, intervaleX);
    };
};

/*  eleShow(): 透明显现动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: start: 初始透明度，取值区间为 [0,1]，类型number
    参数三: end: 终点透明度，取值区间为 [0,1]，类型number
    参数四: interval: 动画时间间隔（ms）
    参数五: step: 动画步数间隔数，最大值1
    参数五: callback: 可选，动画完成后的回调函数
*/
function eleShow(htmlElement, start, end, interval, step, callback) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    };
    if ((typeof start) !== 'number' || (typeof end) !== 'number' || (typeof step) !== 'number') {
        console.error('startANDendError: 缺少/类型错误（必须为number类型） start参数 或 end参数 或 step参数');
        return;
    };
    if (start > 1 || start < 0) {
        console.error('startError: start参数的取值区间为 [0,1]');
        return;
    };
    if (end > 1 || end < 0) {
        console.error('endError: end参数的取值区间为 [0,1]');
        return;
    };
    if (step > 1) {
        console.error('stepError: step参数必须小于等于 1');
        return;
    };

    //数据初始化
    let n = 1;
    let finish = false;
    if ((end - start) < 0) n = -1;

    //动画开始
    let showAni = setInterval(() => {
        start += step * n;
        htmlElement.style.opacity = start;
        if (start * n >= end * n) {
            htmlElement.style.opacity = end;
            clearInterval(showAni);
            finish = true;
            if (typeof callback === 'function') callback(finish);
            return;
        };
    }, interval);
};

/*  eleDrag(): 按下拖动动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: direction: 拖动的方向，参数的值为"x" / "y" / "xy"
    参数三: callback: 可选，动画完成后的回调函数
    ------
    按下拖动动画---返回值说明:
    返回一个对象，其包含closeDrag方法， 该方法可以关掉对该元素的拖动监听事件
    object.closeDrag()
    和包含recoverDrag方法 该方法可以恢复对该元素的拖动监听事件
    object.recoverDrag()
*/
function eleDrag(htmlElement, direction, callback) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    };
    if (direction !== 'x' && direction !== 'y' && direction !== 'xy') {
        console.error('directionError: direction参数的值为"x" / "y" / "xy"');
        return;
    };

    //初始化数据
    let finish = false;
    let mouseX = 0;
    let mouseY = 0;
    let addTranslateX = '';
    let addTranslateY = '';
    let addRotateX = '';
    let addRotateY = '';
    let addRotateZ = '';
    let addScale = '';
    let htmlTranslateX = TRANSLATEX_REGEXP.exec(htmlElement.style.transform);
    let htmlTranslateY = TRANSLATEY_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateX = ROTATEX_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateY = ROTATEY_REGEXP.exec(htmlElement.style.transform);
    let htmlRotateZ = ROTATEZ_REGEXP.exec(htmlElement.style.transform);
    let htmlScale = SCALE_REGEXP.exec(htmlElement.style.transform);
    if (htmlTranslateX !== null) {
        addTranslateX = htmlTranslateX[0];
        mouseX = htmlTranslateX[0].replace(/[translateX\(px\)]/ig, '');
    }
    if (htmlTranslateY !== null) {
        addTranslateY = htmlTranslateY[0];
        mouseY = htmlTranslateY[0].replace(/[translateY\(px\)]/ig, '');
    };
    if (htmlRotateX !== null) addRotateX = htmlRotateX[0];
    if (htmlRotateY !== null) addRotateY = htmlRotateY[0];
    if (htmlRotateZ !== null) addRotateZ = htmlRotateZ[0];
    if (htmlScale !== null) addScale = htmlScale[0];

    //拖动功能代码
    let drag = (e) => {
        let mX = e.pageX - htmlElement.offsetLeft - mouseX;
        let mY = e.pageY - htmlElement.offsetTop - mouseY;
        htmlElement.onmousemove = (e) => {
            mouseX = e.pageX - htmlElement.offsetLeft - mX;
            mouseY = e.pageY - htmlElement.offsetTop - mY;
            if (direction === 'xy') {
                htmlElement.style.transform = `translateX(${mouseX}px) translateY(${mouseY}px)` + addRotateX + addRotateY + addRotateZ + addScale;
            } else if (direction === 'x') {
                htmlElement.style.transform = `translateX(${mouseX}px)` + addTranslateY + addRotateX + addRotateY + addRotateZ + addScale;
            } else {
                htmlElement.style.transform = addTranslateX + `translateY(${mouseY}px)` + addRotateX + addRotateY + addRotateZ + addScale;
            };
        };
    };
    let stop = () => {
        htmlElement.onmousemove = '';
        finish = true;
        if (typeof callback === 'function') callback(finish);
    };
    let leave = () => {
        htmlElement.onmousemove = '';
    };
    htmlElement.addEventListener('mousedown', drag);
    htmlElement.addEventListener('mouseup', stop);
    htmlElement.addEventListener('mouseleave', leave);

    return {
        closeDrag() {
            htmlElement.removeEventListener('mousedown', drag);
            htmlElement.removeEventListener('mouseup', stop);
            htmlElement.removeEventListener('mouseleave', leave);
            htmlElement.onmousemove = '';
        },
        recoverDrag() {
            htmlElement.addEventListener('mousedown', drag);
            htmlElement.addEventListener('mouseup', stop);
            htmlElement.addEventListener('mouseleave', leave);
        }
    }
};

/*  dropSpringback(): 掉落回弹动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: start: 初始位置，值为小于等于0，类型number
    参数三: end: 终点位置，类型number
    参数四: step: 掉落后回弹的次数取值范围为[0,3]
    参数五: callback: 可选，动画完成后的回调函数
*/
function dropSpringback(htmlElement, start, end, step, callback) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    };
    if ((typeof start) !== 'number' || (typeof end) !== 'number' || (typeof step) !== 'number') {
        console.error('startANDendError: 缺少/类型错误（必须为number类型） start参数 或 end参数 或 step参数');
        return;
    };
    if (start > 0) {
        console.error('startError: start参数的值为小于等于0');
        return;
    };
    if (end < 0 && start === 0) {
        console.error('endError: 当start为0时，end参数的值为大于等于0');
        return;
    };

    //初始化数据
    let finish = false;
    if (step > 3 || step < 0) {
        step = 3;
        console.warn('stepWarning: step参数取值范围为[0,3]');
    };

    // 动画开始
    let p = new Promise((resolve, reject) => {
        eleMov(htmlElement, start, end, 300, 'v', 'linear', 0, 5, () => {
            if (step === 0) {
                finish = true;
                if (typeof callback === 'function') callback(finish);
                resolve(['out']);
            } else {
                resolve([start, end]);
            };
        });
    });
    p.then(([start, end]) => {
        if (start !== 'out') {
            return new Promise((resolve, reject) => {
                if (start >= 0) {
                    start += end / 2;
                } else {
                    start = start / 2;
                };
                eleMov(htmlElement, end, start, 200, 'v', 'ease', 0, 5, () => {
                    resolve([start, end]);
                });
            });
        } else {
            return;
        };
    }).then(([start, end]) => {
        return new Promise((resolve, reject) => {
            eleMov(htmlElement, start, end, 200, 'v', 'linear', 0, 5, () => {
                if (step === 1) {
                    finish = true;
                    if (typeof callback === 'function') callback(finish);
                    resolve(['out']);
                } else {
                    resolve([start, end]);
                };
            });
        });
    }).then(([start, end]) => {
        if (start !== 'out') {
            return new Promise((resolve, reject) => {
                if (start >= 0) {
                    start += end / 4;
                } else {
                    start = start / 2;
                };
                eleMov(htmlElement, end, start, 100, 'v', 'ease', 0, 5, () => {
                    resolve([start, end]);
                });
            });
        } else {
            return;
        };
    }).then(([start, end]) => {
        return new Promise((resolve, reject) => {
            eleMov(htmlElement, start, end, 100, 'v', 'linear', 0, 5, () => {
                if (step === 2) {
                    finish = true;
                    if (typeof callback === 'function') callback(finish);
                    resolve(['out']);
                } else {
                    resolve([start, end]);
                };
            });
        });
    }).then(([start, end]) => {
        if (start !== 'out') {
            return new Promise((resolve, reject) => {
                if (start >= 0) {
                    start += end / 8;
                } else {
                    start = start / 2;
                };
                eleMov(htmlElement, end, start, 100, 'v', 'linear', 0, 5, () => {
                    resolve([start, end]);
                });
            });
        } else {
            return;
        };
    }).then(([start, end]) => {
        eleMov(htmlElement, start, end, 50, 'v', 'linear', 0, 5, () => {
            finish = true;
            if (typeof callback === 'function') callback(finish);
            return;
        });
    }).catch((reason) => {
        console.error(reason);
    });
};

/*  fadeout(): 淡出动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: direction: 动画方向，值为"toup" / "todown" / "toleft" / "toright"，类型string
    参数三: callback: 可选，动画完成后的回调函数
*/
function fadeout(htmlElement, direction, callback) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    };
    if (direction !== 'toup' && direction !== 'todown' && direction !== 'toleft' && direction !== 'toright') {
        console.error('directionError: direction参数的值为"toup" / "todown" / "toleft" / "toright"');
        return;
    };

    //初始化数据
    let n = 1;
    let d = '';
    let finish = false;
    let flagMov = false;
    let flagShow = false;
    switch (direction) {
        case 'todown': d = 'v'; break;
        case 'toright': d = 'h'; break;
        case 'toup': d = 'v'; n = -1; break;
        case 'toleft': d = 'h'; n = -1; break;
    };

    //动画开始
    eleMov(htmlElement, 0, 50 * n, 1000, d, 'ease', 0, 2, () => {
        flagMov = true;
        if (flagMov && flagShow) {
            finish = true;
            if (typeof callback === 'function') callback(finish);
        };
    });
    eleShow(htmlElement, 1, 0, 16, 0.05, () => {
        flagShow = true;
        if (flagMov && flagShow) {
            finish = true;
            if (typeof callback === 'function') callback(finish);
        };
    });
};

/*  fadein(): 淡入动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: direction: 动画方向，值为"toup" / "todown" / "toleft" / "toright"，类型string
    参数三: callback: 可选，动画完成后的回调函数
*/
function fadein(htmlElement, direction, callback) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    };
    if (direction !== 'toup' && direction !== 'todown' && direction !== 'toleft' && direction !== 'toright') {
        console.error('directionError: direction参数的值为"toup" / "todown" / "toleft" / "toright"');
        return;
    };

    //初始化数据
    let n = 1;
    let d = '';
    let finish = false;
    let flagMov = false;
    let flagShow = false;
    htmlElement.style.opacity = 0;
    switch (direction) {
        case 'toup': {
            d = 'v';
            htmlElement.style.transform = 'translateY(50px)';
            break;
        };
        case 'toleft': {
            d = 'h';
            htmlElement.style.transform = 'translateX(50px)';
            break;
        };
        case 'todown': {
            d = 'v';
            n = -1;
            htmlElement.style.transform = 'translateY(-50px)';
            break;
        };
        case 'toright': {
            d = 'h';
            n = -1;
            htmlElement.style.transform = 'translateX(-50px)';
            break;
        };
    };

    //动画开始
    eleMov(htmlElement, 50 * n, 0, 1500, d, 'ease', 0, 2, () => {
        flagMov = true;
        if (flagMov && flagShow) {
            finish = true;
            if (typeof callback === 'function') callback(finish);
        };
    });
    eleShow(htmlElement, 0, 1, 16, 0.02, () => {
        flagShow = true;
        if (flagMov && flagShow) {
            finish = true;
            if (typeof callback === 'function') callback(finish);
        };
    });
};

/*  undulate(): 字体跳动动画函数---参数说明:
    参数一: htmlElementArray: 需要操作的html元素数组
    参数二: undulate: 上下波动幅度距离，类型number
    参数三: direction: 移动方向，值为'h'水平方向 或者 'v'垂直方向，类型string
    参数四: callback: 可选，动画完成后的回调函数
*/
function undulate(htmlElementArray, undulateDuration, direction, callback) {
    //判断参数是否合法
    if (!(htmlElementArray instanceof NodeList)) {
        console.error('htmlElementArrayError: 参数一错误，请传入html元素数组');
        return;
    };
    if (typeof undulateDuration !== 'number') {
        console.error('undulateError: undulate参数的数据类型需为 number');
        return;
    };
    if (direction !== 'h' && direction !== 'v') {
        console.error('directionError: 输入合法方向值"h" / "v"');
        return;
    };

    //初始化数据
    let finish = false;

    //动画完成后的执行回调函数
    let cb = (i) => {
        if (i === htmlElementArray.length - 1) {
            finish = true;
            if (typeof callback === 'function') callback(finish);
        };
    };

    //动画开始
    for (let i = 0; i < htmlElementArray.length; i++) {
        setTimeout(() => {
            eleMov(htmlElementArray[i], 0, -undulateDuration, 500, direction, 'linear', 0, 2, () => {
                eleMov(htmlElementArray[i], -undulateDuration, 0, 500, direction, 'linear', 0, 2, () => {
                    eleMov(htmlElementArray[i], 0, undulateDuration, 500, direction, 'linear', 0, 2, () => {
                        eleMov(htmlElementArray[i], undulateDuration, 0, 500, direction, 'linear', 0, 2, cb.bind(this, i));
                    });
                });
            });
        }, i * 100);
    };
};

/*  fontFadein(): 字体淡入动画---参数说明:
    参数一: htmlElementArray: 需要操作的html元素数组
    参数二: direction: 动画方向，值为"toup" / "todown" / "toleft" / "toright"，类型string
    参数三: callback: 可选，动画完成后的回调函数
*/
function fontFadein(htmlElementArray, direction, callback) {
    //判断参数是否合法
    if (!(htmlElementArray instanceof NodeList)) {
        console.error('htmlElementArrayError: 参数一错误，请传入html元素数组');
        return;
    }
    if (direction !== 'toup' && direction !== 'todown' && direction !== 'toleft' && direction !== 'toright') {
        console.error('directionError: direction参数的值为"toup" / "todown" / "toleft" / "toright"');
        return;
    };

    //初始化数据
    let finish = false;

    //动画开始
    for (let i = 0; i < htmlElementArray.length; i++) {
        setTimeout(() => {
            htmlElementArray[i].style.display = 'inline-block';
            fadein(htmlElementArray[i], direction, () => {
                if (i === htmlElementArray.length - 1) {
                    finish = true;
                    if (typeof callback === 'function') callback(finish);
                };
            })
        }, i * 100);
    };
};

/*  fontFadeout(): 字体淡出动画---参数说明:
    参数一: htmlElementArray: 需要操作的html元素数组
    参数二: direction: 动画方向，值为"toup" / "todown" / "toleft" / "toright"，类型string
    参数三: callback: 可选，动画完成后的回调函数
*/
function fontFadeout(htmlElementArray, direction, callback) {
    //判断参数是否合法
    if (!(htmlElementArray instanceof NodeList)) {
        console.error('htmlElementArrayError: 参数一错误，请传入html元素数组');
        return;
    };
    if (direction !== 'toup' && direction !== 'todown' && direction !== 'toleft' && direction !== 'toright') {
        console.error('directionError: direction参数的值为"toup" / "todown" / "toleft" / "toright"');
        return;
    };

    //初始化数据
    let finish = false;

    //动画开始
    for (let i = 0; i < htmlElementArray.length; i++) {
        setTimeout(() => {
            fadeout(htmlElementArray[i], direction, () => {
                htmlElementArray[i].style.display = 'none';
                if (i === htmlElementArray.length - 1) {
                    finish = true;
                    if (typeof callback === 'function') callback(finish);
                };
            });
        }, i * 100);
    };
};

/*  fontScalein(): 字体缩放出现动画---参数说明:
    参数一: htmlElementArray: 需要操作的html元素数组
    参数二: callback: 可选，动画完成后的回调函数
*/
function fontScalein(htmlElementArray, callback) {
    //判断参数是否合法
    if (!(htmlElementArray instanceof NodeList)) {
        console.error('htmlElementArrayError: 参数一错误，请传入html元素数组');
        return;
    };

    //初始化数据
    let finish = false;

    //动画开始
    for (let i = 0; i < htmlElementArray.length; i++) {
        setTimeout(() => {
            eleScale(htmlElementArray[i], [0, 1], [0, 1], 200, 'linear', 0.01, () => {
                if (i === htmlElementArray.length - 1) {
                    finish = true;
                    if (typeof callback === 'function') callback(finish);
                };
            });
        }, i * 100);
    };
};

/*  fontScaleout(): 字体缩放消失动画---参数说明:
    参数一: htmlElementArray: 需要操作的html元素数组
    参数二: callback: 可选，动画完成后的回调函数
*/
function fontScaleout(htmlElementArray, callback) {
    //判断参数是否合法
    if (!(htmlElementArray instanceof NodeList)) {
        console.error('htmlElementArrayError: 参数一错误，请传入html元素数组');
        return;
    };

    //初始化数据
    let finish = false;

    //动画开始
    for (let i = 0; i < htmlElementArray.length; i++) {
        setTimeout(() => {
            eleScale(htmlElementArray[i], [1, 0], [1, 0], 200, 'linear', 0.01, () => {
                if (i === htmlElementArray.length - 1) {
                    finish = true;
                    if (typeof callback === 'function') callback(finish);
                };
            });
        }, i * 100);
    };
};

/*  windmill(): 风车动画---参数说明:
    参数一: htmlElement: 需要操作的html元素
    参数二: startDeg: 旋转起始角度，一般为0，类型number
    参数三: step: 旋转步数间隔数，数值越大旋转速度越快，类型number
    参数四: direction: 旋转方向，类型string
    参数五: n: 1为顺时针，-1为逆时针，默认值为1，类型number
    ------
    windmill(): 风车动画---返回值说明:
    返回一个对象，其包含stopWindmill方法， 该方法可以停止元素旋转
    object.stopWindmill()
    和包含openWindmill方法 该方法可以启动元素旋转
    object.openWindmill()
*/
function windmill(htmlElement, startDeg, step, direction, n = 1) {
    //判断参数是否合法
    if (!(htmlElement instanceof Element)) {
        console.error('htmlElementError: 参数一错误，请传入html元素');
        return;
    };
    if (typeof startDeg !== 'number') {
        console.error('startDegError: startDeg参数的数据类型需为 number');
        return;
    };
    if (typeof step !== 'number') {
        console.error('rotateIntervalError: rotateInterval参数的数据类型需为 number');
        return;
    };
    if (direction !== 'z' && direction !== 'x' && direction !== 'y') {
        console.error('directionError: 输入合法旋转轴值"x" / "y" / "z"');
        return;
    };
    if (n !== 1 && n !== -1) {
        console.error('nError: n参数的值为"1" / "-1"');
        return;
    };

    //初始化数据
    let i = 0;
    let start = 0;
    let end = 0;
    let addTranslateX = '';
    let addTranslateY = '';
    let addRotateX = '';
    let addRotateY = '';
    let addRotateZ = '';
    let addScale = '';
    let flag = { stop: false };
    let _flag = {};
    Object.defineProperty(_flag, 'stop', {
        get() {
            return flag.stop;
        },
        set(value) {
            flag.stop = value;
        }
    });

    //动画开始
    let rotate = (_flag, start, end, i, startDeg) => {
        if (_flag.stop) {
            let htmlTranslateX = TRANSLATEX_REGEXP.exec(htmlElement.style.transform);
            let htmlTranslateY = TRANSLATEY_REGEXP.exec(htmlElement.style.transform);
            let htmlRotateX = ROTATEX_REGEXP.exec(htmlElement.style.transform);
            let htmlRotateY = ROTATEY_REGEXP.exec(htmlElement.style.transform);
            let htmlRotateZ = ROTATEZ_REGEXP.exec(htmlElement.style.transform);
            let htmlScale = SCALE_REGEXP.exec(htmlElement.style.transform);
            if (htmlTranslateX !== null) addTranslateX = htmlTranslateX[0];
            if (htmlTranslateY !== null) addTranslateY = htmlTranslateY[0];
            if (htmlRotateX !== null) addRotateX = htmlRotateX[0];
            if (htmlRotateY !== null) addRotateY = htmlRotateY[0];
            if (htmlRotateZ !== null) addRotateZ = htmlRotateZ[0];
            if (htmlScale !== null) addScale = htmlScale[0];
            htmlElement.style.transform = addTranslateX + addTranslateY + addRotateX + addRotateY + addRotateZ + addScale;
            return;
        } else {
            start = 45 * n * i + startDeg;
            end = 45 * n + start;
            eleRotate(htmlElement, start, end, 1000, direction, 'linear', step, () => {
                if (end < 360 + startDeg) {
                    i++;
                    rotate(_flag, start, end, i, startDeg);
                } else {
                    i = 0;
                    start = 0;
                    end = 0;
                    rotate(_flag, start, end, i, startDeg);
                };
            });
        };
    };
    rotate(_flag, start, end, i, startDeg);

    return {
        stopWindmill() {
            _flag.stop = true;
        },
        openWindmill() {
            _flag.stop = false;
            if (direction === 'z') {
                let htmlRotateZ = ROTATEZ_REGEXP.exec(htmlElement.style.transform);
                addRotateZ = /\d+/i.exec(htmlRotateZ[0]);
                rotate(_flag, start, end, i, parseInt(addRotateZ[0]) * n);
            } else if (direction === 'x') {
                let htmlRotateX = ROTATEX_REGEXP.exec(htmlElement.style.transform);
                addRotateX = /\d+/i.exec(htmlRotateX[0]);
                rotate(_flag, start, end, i, parseInt(addRotateX[0]) * n);
            } else {
                let htmlRotateY = ROTATEY_REGEXP.exec(htmlElement.style.transform);
                addRotateY = /\d+/i.exec(htmlRotateY[0]);
                rotate(_flag, start, end, i, parseInt(addRotateY[0]) * n);
            };
        }
    }
};